package com.example.factory;

import com.alibaba.druid.util.StringUtils;
import com.example.annotation.*;
import org.reflections.Reflections;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author kangshuai
 * 预备：自定义注解：@Service（标记Service层实现类）、@Component（标记普通类型）、
 * @Repository（标记Dao层实现类）、@Autowired（标记属性）、
 * @Configuration（标记配置类，定义扫描包结构）、@Transactional（标记事务处理） 0、定义2个map集合，singletonObject（外部获取指定bean对象）、map3（内部处理bean对象用）
 * 1、构建静态代码块 ：随着类的加载而执行，而且只执行一次
 * 在当前工厂类标记注解 @Configuration ,根据传值 加载指定目录下下的所有类，默认记载根目录下第一级目录文件
 * 获取标记 @Service、@Component、@Repository 的类，并处理放进 map集合中（map3）
 * 2、对map3进行三次处理：（因为当前项目不涉及循环依赖问题，所以未进行该层面实现）
 * （1）将获取出来的类全部放入map3中，判断是否创建实现接口，判断注解是否传值（如果传值以该值定义 id，如果未传值则以类明，并且首字母小写定义传值），等待进一步处理
 * （2）对map3中的每个类，判断属性是否标记@Autowired注解，并利用反射内省开启暴力访问，直接向对象属性赋值，无需在类中设置方法赋值，用来赋值的对象是从map3中获取的原有属性
 * （3）在所有类的属性赋值完毕，进行代理类处理(实现几口的使用jdk，未实现接口的使用cglib)，把map3向singletonObject转移，转移完毕，清空map3
 * 3、在该类定义首字母转小方法：lowerFirstCase()
 * 4、定义似有方法:getInnerBean(),在本类中获取中指定id对象
 * 5、定义公共方法:getBean(),在外部获取指定id对象
 */
@Configuration(value = "com.example")
public class BeanFactory {

    private static Map<String, Object> singletonObject = new HashMap<>();
    private static Map<String, Object> map3 = new HashMap<>();


    static {

        Class beanFactoryClass = BeanFactory.class;

        Configuration annotation = (Configuration) beanFactoryClass.getAnnotation(Configuration.class);

        Reflections reflections = new Reflections(annotation == null || StringUtils.isEmpty(annotation.value()) ?
                beanFactoryClass.getPackage().getName().split("\\.")[0] :
                annotation.value());

        //获取标记@Component注解全部类
        Set<Class<?>> componentClasses = reflections.getTypesAnnotatedWith(Component.class);
        //获取标记@Repository注解全部类
        Set<Class<?>> repositoryClasses = reflections.getTypesAnnotatedWith(Repository.class);
        //获取标记@Service注解全部类
        Set<Class<?>> serviceClasses = reflections.getTypesAnnotatedWith(Service.class);

        try {
            componentHandle(componentClasses);
            repositoryHandle(repositoryClasses);
            serviceHandle(serviceClasses);
            classFidIdHandle();
            transactionalHandle();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 将获取出来的@Service类全部放入map3中，判断是否创建实现接口，
     * 判断注解是否传值（如果传值以该值定义 id，
     * 如果未传值则以类明，并且首字母小写定义传值），等待进一步处理
     *
     * @param classSet
     * @throws Exception
     */
    private static void serviceHandle(Set<Class<?>> classSet) throws Exception {
        String map3KeyName = "";
        for (Class<?> serviceClass : classSet) {
            //获取该类所实现的所有接口
            Class<?>[] interfaces = serviceClass.getInterfaces();
            for (Class<?> anInterface : interfaces) {
                map3KeyName = StringUtils.isEmpty(serviceClass.getAnnotation(Service.class).value()) ?
                        lowerFirstCase(anInterface.getName().substring(anInterface.getName().lastIndexOf(".") + 1))
                        : serviceClass.getAnnotation(Service.class).value();
                map3.put(map3KeyName, serviceClass.getDeclaredConstructor().newInstance());
            }
        }
    }

    /**
     * 将获取出来的@Repository类全部放入map3中，判断是否创建实现接口，
     * 判断注解是否传值（如果传值以该值定义 id，
     * 如果未传值则以类明，并且首字母小写定义传值），等待进一步处理
     *
     * @param classSet
     * @throws Exception
     */
    private static void repositoryHandle(Set<Class<?>> classSet) throws Exception {
        String map3KeyName = "";
        for (Class<?> repositoryClass : classSet) {
            //获取该类所实现的所有接口
            Class<?>[] interfaces = repositoryClass.getInterfaces();
            for (Class<?> anInterface : interfaces) {
                map3KeyName = StringUtils.isEmpty(repositoryClass.getAnnotation(Repository.class).value()) ?
                        lowerFirstCase(anInterface.getName().substring(anInterface.getName().lastIndexOf(".") + 1))
                        : repositoryClass.getAnnotation(Repository.class).value();
                map3.put(map3KeyName, repositoryClass.getDeclaredConstructor().newInstance());
            }
        }
    }

    /**
     * 将获取出来的@Component类全部放入map3中，判断是否创建实现接口，
     * 判断注解是否传值（如果传值以该值定义 id，
     * 如果未传值则以类明，并且首字母小写定义传值），等待进一步处理
     *
     * @param classSet
     * @throws Exception
     */
    private static void componentHandle(Set<Class<?>> classSet) throws Exception {
        String map3KeyName = "";
        for (Class<?> componentClass : classSet) {
            map3KeyName = StringUtils.isEmpty(componentClass.getAnnotation(Component.class).value()) ?
                    lowerFirstCase(componentClass.getName().substring(componentClass.getName().lastIndexOf(".") + 1))
                    : componentClass.getAnnotation(Component.class).value();
            map3.put(map3KeyName, componentClass.getDeclaredConstructor().newInstance());
        }
    }

    /**
     * 对map3中的每个类，判断属性是否标记@Autowired注解，
     * 并利用反射内省向类型对象属性赋值，
     * 用来赋值的对象是从map3中获取的原有属性
     *
     * @throws Exception
     */
    private static void classFidIdHandle() throws Exception {
        String map3KeyName = "";
        for (String map3Key : map3.keySet()) {
            for (Field field : map3.get(map3Key).getClass().getDeclaredFields()) {
                if (field.getAnnotation(Autowired.class) != null) {
                    map3KeyName = StringUtils.isEmpty(field.getAnnotation(Autowired.class).value()) ?
                            lowerFirstCase(field.getType().getName().substring(field.getType().getName().lastIndexOf(".") + 1))
                            : field.getAnnotation(Autowired.class).value();
                    field.setAccessible(true);
                    field.set(map3.get(map3Key), map3.get(map3KeyName));
                    map3.put(map3Key, map3.get(map3Key));
                }
            }
        }
    }

    /**
     * 在所有类的属性赋值完毕，进行代理类处理，
     * 把map3向singletonObject转移，转移完毕，清空map3
     *
     * @throws Exception
     */
    private static void transactionalHandle() throws Exception {
        Object object;
        for (String map3key : map3.keySet()) {
            if (map3.get(map3key).getClass().getAnnotation(Transactional.class) != null) {
                ProxyFactory proxyFactory = (ProxyFactory) getInnerBean("proxyFactory");
                if (proxyFactory.getClass().getInterfaces().length > 0) {
                    object = proxyFactory.getJdkProxy(map3.get(map3key));
                } else {
                    object = proxyFactory.getCglibProxy(map3.get(map3key));
                }
                map3.put(map3key, object);
            }
            singletonObject.put(map3key, map3.get(map3key));
        }
        map3.clear();
    }

    /**
     * 对外提供获取实例对象的接口（根据id获取）
     *
     * @param id
     * @return
     */
    public static Object getBean(String id) {
        return singletonObject.get(id);
    }

    /**
     * 对内提供获取实例对象的接口（根据id获取）
     *
     * @param id
     * @return
     */
    private static Object getInnerBean(String id) {
        return map3.get(id);
    }


    /**
     * 首字母小写
     *
     * @param str
     * @return
     */
    private static String lowerFirstCase(String str) {
        if (Character.isLowerCase(str.charAt(0))) {
            return str;
        }
        char[] chars = str.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

    /**
     * 首字母大写
     *
     * @param str
     * @return
     */
    private static String upperFirstCase(String str) {
        if (Character.isUpperCase(str.charAt(0))) {
            return str;
        }
        char[] chars = str.toCharArray();
        chars[0] -= 32;
        return String.valueOf(chars);
    }

}